package core;
import core.queue.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;

public class Queue {
    private ListenerAction listener = null;
    private final Map<String, BackActions> callback = new HashMap<>();
    private final List<String> totalLine = new ArrayList<>();
    private final List<String> avoidLine = new ArrayList<>();
    private final List<String> validLine = new ArrayList<>();

    private String current = null;
    private int status = -1;
    private int code = 255;

    private String getCodeStr(){
        code++;
        return Integer.toHexString(code);
    }

    private boolean doAction(String name){
        return doAction(name, false);
    }
    private boolean doAction(String name, boolean after){
        if(!callback.containsKey(name)) return false;
        return callback.get(name).doAction(this, after);
    }

    public Queue listen(ListenerAction listener){
        this.listener = listener;
        return this;
    }

    public String getCurrent(){
        return current;
    }

    public Queue add(QueueAction before){
        return add(before, null);
    }
    public Queue add(String name, QueueAction before){
        return add(name, before, null);
    }
    public Queue add(QueueAction before, QueueAction after){
        return add(getCodeStr(), before, after);
    }

    public Queue add(String name, QueueAction before, QueueAction after){
        BackActions action = new BackActions();
        if(before != null) {
            action.setOnBefore(before);
        }
        if(after != null){
            action.setOnAfter(after);
        }
        callback.put(name, action);
        totalLine.add(name);
        return this;
    }

    public void next(){
        if(totalLine.isEmpty() && current == null){
            return ;
        }
        if(!totalLine.isEmpty()){
            next(totalLine.get(0));
        }else{
            next(null);
        }
    }
    public void next(String name){
        String after = current;
        String before = name;
        boolean paused = false;

        if(status == -1){
            status = 0;
        }else{
            paused = doAction(after, true);
            validLine.add(after);
            current = null;
        }

        if(listener != null && after != null){
            String prior = listener.apply(this, before, after);
            if(prior != null && totalLine.contains(prior)){
                before = prior;
            }
        }
        if(before != null && totalLine.contains(before) && !paused){
            current = before;
            int index = totalLine.indexOf(before);
            for(int i = 0; i < index; i++){
                if(!avoidLine.contains(totalLine.get(i))){
                    this.avoidLine.add(totalLine.get(i));
                }
            }
            totalLine.subList(0, index + 1).clear();
            paused = !doAction(before, false);
            if(!paused) next();
        }
        if(totalLine.isEmpty() && current != null){
            status = 1;
        }
    }

    public Queue disable(String name){
        if(status == 1 && this.totalLine.contains(name)){
            avoidLine.remove(name);
            totalLine.remove(name);
            avoidLine.add(name);
        }
        return this;
    }

    public Queue enable(String name){
        return enable(name, false);
    }

    public Queue enable(String name, boolean after){
        avoidLine.remove(name);
        totalLine.remove(name);
        if(after){
            totalLine.add(name);
        }else{
            totalLine.add(0, name);
        }
        return this;
    }

    public boolean isDone(){
        return status == 1;
    }

    public boolean isWait(){
        return status == 0;
    }

    public boolean isInit(){
        return status == -1;
    }

    public Queue init(){
        status = -1;
        totalLine.clear();
        validLine.clear();
        avoidLine.clear();
        callback.clear();
        listener = null;
        return this;
    }

    public Map<String, List<String>> debug(){
        return new HashMap<String, List<String>>(){{
            put("total_line", totalLine);
            put("valid_line", validLine);
            put("avoid_line", avoidLine);
        }};
    }
}
